﻿using System;
using System.IO;
using BC = Org.BouncyCastle;

namespace XOAuth
{
	public static class KEY
	{
		private static string _CurrentDirectory;
		public static string CurrentDirectory
		{
			get
			{
				if (PathRootNull)
				{
					_CurrentDirectory = Environment.CurrentDirectory;
				}
				return _CurrentDirectory;
			}
		}

		public static bool PathRootNull
		{
			get { return _CurrentDirectory == null; }
		}
		public static void SetCurrentDirectory(string path)
		{
			_CurrentDirectory = path;
		}
		public static void SetCurrentDirectory()
		{
			SetCurrentDirectory(Environment.CurrentDirectory);
		}

		private static string LocalFile(string name)
		{
			return Path.Combine(CurrentDirectory, name);
		}

		private const int KEY_STRENGTH = 2048;
		private const int KEY_PUB_EXPONENT = 65537;
		private const int KEY_CERTAINTY = 97;
		private static BC.Crypto.AsymmetricCipherKeyPair NewKeyPair()
		{
			var gen = new BC.Crypto.Generators.RsaKeyPairGenerator();
			var param = new BC.Crypto.Parameters.RsaKeyGenerationParameters(
				BC.Math.BigInteger.ValueOf(KEY_PUB_EXPONENT)
				, new BC.Security.SecureRandom()
				, KEY_STRENGTH
				, KEY_CERTAINTY
				);
			gen.Init(param);
			var keyPair = gen.GenerateKeyPair();
			return keyPair;
		}

		public static void InitKeys()
		{
			if (KeyPairExists) return;
			GenKeys();
		}

		private static string PublicKeyPemFile
		{
			get { return LocalFile("public.pem"); }
		}
		private static string PrivateKeyPemFile
		{
			get { return LocalFile("private.pem"); }
		}
		private static bool KeyPairExists
		{
			get
			{
				return File.Exists(PrivateKeyPemFile) && File.Exists(PublicKeyPemFile);
			}
		}

		private static void ClearKeys()
		{
			File.Delete(PublicKeyPemFile);
			File.Delete(PrivateKeyPemFile);
		}
		private static void GenKeys()
		{
			ClearKeys();

			var keyPair = NewKeyPair();
			var privateKey = BC.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
			var privateKeyData = privateKey.GetEncoded("UTF-8");
			using (StreamWriter writer = new StreamWriter(PrivateKeyPemFile))
			{
				var w = new BC.OpenSsl.PemWriter(writer);
				var pem = new BC.Utilities.IO.Pem.PemObject("RSA PRIVATE KEY", privateKeyData);
				w.WriteObject(pem);
				writer.Close();
			}

			var publicKey = Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
			var publicKeyData = publicKey.GetEncoded("UTF-8");
			using (StreamWriter writer = new StreamWriter(PublicKeyPemFile))
			{
				var w = new Org.BouncyCastle.OpenSsl.PemWriter(writer);
				var pem = new BC.Utilities.IO.Pem.PemObject("RSA PUBLIC KEY", publicKeyData);
				w.WriteObject(pem);
				writer.Close();
			}
		}

		private static string GetPublicKeyString()
		{
			string base64 = null;
			using (var reader = new StreamReader(PublicKeyPemFile, System.Text.Encoding.UTF8))
			{
				var r = new BC.OpenSsl.PemReader(reader);
				var result = r.ReadPemObject();
				base64 = Convert.ToBase64String(result.Content);
				reader.Close();
			}
			return base64;
		}
		private static string _Public = null;
		public static string Public
		{
			get
			{
				if (_Public == null)
				{
					_Public = String.Empty;
					lock (_Public)
					{
						InitKeys();
						_Public = GetPublicKeyString();
					}
				}
				return _Public;
			}
		}

		private static string GetPrivateKeyString()
		{
			string base64 = null;
			using (var reader = new StreamReader(PrivateKeyPemFile, System.Text.Encoding.UTF8))
			{
				var r = new BC.OpenSsl.PemReader(reader);
				var result = r.ReadPemObject();
				base64 = Convert.ToBase64String(result.Content);
				reader.Close();
			}
			return base64;
		}

		private static string _PrivateKey = null;
		private static string Privatekey
		{
			get
			{
				if (_PrivateKey == null)
				{
					_PrivateKey = String.Empty;
					lock(_PrivateKey)
					{
						_PrivateKey = GetPrivateKeyString();
					}
				}
				return _PrivateKey;
			}
		}

		private static BC.Crypto.AsymmetricKeyParameter _PrivateKeyParameter = null;
		public static byte[] DecryptOaep(byte[] content)
		{
			if (_PrivateKeyParameter == null)
			{
				_PrivateKeyParameter = BC.Security.PrivateKeyFactory.CreateKey(Convert.FromBase64String(Privatekey));
			}
			var engine = new BC.Crypto.Encodings.OaepEncoding(new BC.Crypto.Engines.RsaEngine());
			engine.Init(false, _PrivateKeyParameter);
			var result = engine.ProcessBlock(content, 0, content.Length);
			return result;
		}
		public static byte[] DecryptOaep(string base64)
		{
			return DecryptOaep(Convert.FromBase64String(base64));
		}

		public static byte[] EncryptOaep(byte[] content, BC.Crypto.AsymmetricKeyParameter publicKey)
		{
			var engine = new BC.Crypto.Encodings.OaepEncoding(new BC.Crypto.Engines.RsaEngine());
			engine.Init(true, publicKey);
			var result = engine.ProcessBlock(content, 0, content.Length);
			return result;
		}

		public static string EncryptOaep(string text, BC.Crypto.AsymmetricKeyParameter publicKey)
		{
			var content = System.Text.Encoding.UTF8.GetBytes(text ?? String.Empty);
			var r = EncryptOaep(content, publicKey);
			return Convert.ToBase64String(r);
		}

		private static BC.Crypto.AsymmetricKeyParameter _PublicKeyParameter = null;
		public static string EncryptOaep(string text)
		{
			if (_PublicKeyParameter == null)
			{
				_PublicKeyParameter = BC.Security.PublicKeyFactory.CreateKey(Convert.FromBase64String(Public));
			}
			var content = System.Text.Encoding.UTF8.GetBytes(text ?? String.Empty);
			var r = EncryptOaep(content, _PublicKeyParameter);
			return Convert.ToBase64String(r);
		}

	}
}
